package cn.tedu.mall.order.service.impl;

import cn.tedu.mall.common.exception.CoolSharkServiceException;
import cn.tedu.mall.common.pojo.domain.CsmallAuthenticationInfo;
import cn.tedu.mall.common.restful.JsonPage;
import cn.tedu.mall.common.restful.ResponseCode;
import cn.tedu.mall.order.mapper.OmsOrderItemMapper;
import cn.tedu.mall.order.mapper.OmsOrderMapper;
import cn.tedu.mall.order.service.IOmsCartService;
import cn.tedu.mall.order.service.IOmsOrderService;
import cn.tedu.mall.order.utils.IdGeneratorUtils;
import cn.tedu.mall.pojo.order.dto.OrderAddDTO;
import cn.tedu.mall.pojo.order.dto.OrderItemAddDTO;
import cn.tedu.mall.pojo.order.dto.OrderListTimeDTO;
import cn.tedu.mall.pojo.order.dto.OrderStateUpdateDTO;
import cn.tedu.mall.pojo.order.model.OmsCart;
import cn.tedu.mall.pojo.order.model.OmsOrder;
import cn.tedu.mall.pojo.order.model.OmsOrderItem;
import cn.tedu.mall.pojo.order.vo.OrderAddVO;
import cn.tedu.mall.pojo.order.vo.OrderDetailVO;
import cn.tedu.mall.pojo.order.vo.OrderListVO;
import cn.tedu.mall.product.service.order.IForOrderSkuService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

//后期秒杀模块也是要生成订单的,需要dubbo调用这个方法
@DubboService
@Service
@Slf4j
public class OmsOrderServiceImpl implements IOmsOrderService {

    @DubboReference
    private IForOrderSkuService dubboSkuService;
    @Autowired
    private IOmsCartService omsCartService;
    @Autowired
    private OmsOrderMapper omsOrderMapper;
    @Autowired
    private OmsOrderItemMapper omsOrderItemMapper;

    @GlobalTransactional
    @Override
    public OrderAddVO addOrder(OrderAddDTO orderAddDTO) {
        OmsOrder order = new OmsOrder();
        BeanUtils.copyProperties(orderAddDTO, order);
        //编写方法生成缺失部分属性（id sn 等）。
        loaderOrder(order);

        //操作数据库。
        List<OrderItemAddDTO> orderItemsDTO = orderAddDTO.getOrderItems();
        // 如果订单参数中没有订单项信息,直接抛出异常,终止程序
        if( orderItemsDTO==null || orderItemsDTO.isEmpty()){
            throw new CoolSharkServiceException
                    (ResponseCode.BAD_REQUEST, "订单中至少包含一件商品");
        }
        List<OmsOrderItem> orderItems = new ArrayList<>();
        for(OrderItemAddDTO orderItemAddDTO : orderItemsDTO){
            OmsOrderItem orderItem = new OmsOrderItem();
            BeanUtils.copyProperties(orderItemAddDTO, orderItem);
            //id orderId
            Long id = IdGeneratorUtils.getDistributeId("order_item");
            orderItem.setId(id);
            orderItem.setOrderId(order.getId());
            orderItems.add(orderItem);
            // 1.减少库存
            // 当前正在标记的对象就是一个包含SkuId和减少库存数的对象
            // 获取skuId
            Long skuId=orderItem.getSkuId();
            // dubbo调用减少库存的方法
            int row=dubboSkuService.reduceStockNum(
                    skuId,orderItem.getQuantity());
            // 判断row的值
            if(row==0){
                // 如果row的值为0,表示库存没有变化,库存不足导致的
                log.error("商品库存不足,skuId:{}",skuId);
                // 抛出异常,终止程序,会触发seata分布式事务的回滚
                throw new CoolSharkServiceException(
                        ResponseCode.BAD_REQUEST,"您要购买的商品库存不足!");
            }
            // 2.删除勾选的购物车商品信息
            OmsCart omsCart=new OmsCart();
            omsCart.setUserId(order.getUserId());
            omsCart.setSkuId(skuId);
            // 执行删除
            omsCartService.removeUserCarts(omsCart);
        }
        // 3.执行新增订单
        omsOrderMapper.insertOrder(order);
        // 4.新增订单项(批量新增集合中的所有订单项数据)
        omsOrderItemMapper.insertOrderItemList(orderItems);
        // 第三部分:准备返回值,返回给前端
        // 实例化返回值类型对象
        OrderAddVO addVO=new OrderAddVO();
        // 给各个属性赋值
        addVO.setId(order.getId());
        addVO.setSn(order.getSn());
        addVO.setCreateTime(order.getGmtCreate());
        addVO.setPayAmount(order.getAmountOfActualPay());
        // 别忘了返回addVO
        return addVO;
    }

    private void loaderOrder(OmsOrder order) {
        Long id = IdGeneratorUtils.getDistributeId("order");
        order.setId(id);
        String sn = UUID.randomUUID().toString();
        order.setSn(sn);
        //dubbo调用时不能同时发送登录用户信息。
        if(order.getUserId()==null){
            order.setUserId(getUserId());
        }
        //缺失的的非空属性
        if(order.getState()==null){
            order.setState(0);
        }
        //时间
        LocalDateTime now = LocalDateTime.now();
        order.setGmtOrder(now);
        order.setGmtCreate(now);
        order.setGmtModified(now);
        // 最后处理实际支付金额的计算,返回给前端,用于和前端计算的金额进行验证
        BigDecimal originalPrice = order.getAmountOfOriginalPrice();
        BigDecimal discount = order.getAmountOfDiscount();
        BigDecimal freight = order.getAmountOfFreight();
        BigDecimal actualPay = originalPrice.add(freight).subtract(discount);
        order.setAmountOfActualPay(actualPay);
    }

    @Override
    public void updateOrderState(OrderStateUpdateDTO orderStateUpdateDTO) {
        OmsOrder order = new OmsOrder();
        BeanUtils.copyProperties(orderStateUpdateDTO, order);
        omsOrderMapper.updateOrderById(order);
    }

    //分页查询当前登录用户在指定时间内的所有订单（订单信息， 订单商品信息）。
    @Override
    public JsonPage<OrderListVO> listOrdersBetweenTimes(OrderListTimeDTO orderListTimeDTO) {
        //1.确定时间范围
        validateTimeAndLoadTime(orderListTimeDTO);
        //2.当前用户信息
        orderListTimeDTO.setUserId(getUserId());
        PageHelper.startPage(orderListTimeDTO.getPage(), orderListTimeDTO.getPageSize());
        List<OrderListVO> orderList = omsOrderMapper.selectOrdersBetweenTimes(orderListTimeDTO);
        return JsonPage.restPage(PageInfo.of(orderList));
    }

    private void validateTimeAndLoadTime(OrderListTimeDTO orderListTimeDTO) {

        LocalDateTime startTime = orderListTimeDTO.getStartTime();
        LocalDateTime endTime = orderListTimeDTO.getEndTime();
        if(startTime==null || endTime==null){
            startTime = LocalDateTime.now().minusMonths(1);
            endTime = LocalDateTime.now();

            orderListTimeDTO.setStartTime(startTime);
            orderListTimeDTO.setEndTime(endTime);
        }else{
            //时区修正，结束时间应晚于开始时间
            if(endTime.toInstant(ZoneOffset.of("+8")).toEpochMilli()<
            startTime.toInstant(ZoneOffset.of("+8")).toEpochMilli()){
                throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST, "结束时间应晚于开始时间");
            }
        }
    }

    @Override
    public OrderDetailVO getOrderDetail(Long id) {
        return null;
    }

    public CsmallAuthenticationInfo getUserInfo(){
        // 编写获取SpringSecurity上下文的代码
        UsernamePasswordAuthenticationToken authenticationToken=
                (UsernamePasswordAuthenticationToken)
                        SecurityContextHolder.getContext().getAuthentication();
        // 为了逻辑严谨,判断一下SpringSecurity获取的用户信息是否为null
        if(authenticationToken == null){
            throw new CoolSharkServiceException(
                    ResponseCode.UNAUTHORIZED,"您没有登录");
        }
        // 从SpringSecurity上下文中获取用户信息
        CsmallAuthenticationInfo csmallAuthenticationInfo=
                (CsmallAuthenticationInfo)
                        authenticationToken.getCredentials();
        // 最后别忘了将用户信息返回
        return csmallAuthenticationInfo;
    }

    //  业务逻辑层中实际需求都是获取用户的id
    // 我们再一个方法,直接返回用户id,方便业务调用
    public Long getUserId(){
        return getUserInfo().getId();
    }
}
